home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 06 - 1990 / 06.10 Oct 90 / LZW Source / lDecomp.p < prev    next >
Encoding:
Text File  |  1989-05-07  |  18.2 KB  |  693 lines  |  [TEXT/MPS ]

  1. {$R-}
  2. {$DEFC DEBUG}
  3. {$SETC DEBUG=TRUE}
  4. PROGRAM LDecomp;
  5.  
  6. { Adaptive LZW decompression }
  7.  
  8. USES
  9.     MemTypes,
  10.     QuickDraw,
  11.     OSIntf,
  12.     ToolIntf,
  13.     PackIntf;
  14.     
  15. CONST
  16.     maxBuff = 8192;        {i/o buffer size}
  17.     tableSize = 16383;    {Table size minus 1, 14 bits for 0-based array}
  18.     noPrev = $7FFF;        {First entry in chain}
  19.     eofChar = -2;        {Got to end of input file}
  20.     endList = -1;        {End of chain}
  21.     empty = -3;            {Table entry is unused}
  22.     clearCode = 256;    {Reserved code signalling adaptive reset}
  23.     maxStack = 4096;    {Handles up to 16MB repetition before overflow}
  24.     
  25. TYPE
  26.     {With some older compilers, you'll need to break the following into
  27.      multiple arrays since they won't allow data structure definitions
  28.      larger than 32K bytes}
  29.     StringTableEntry = RECORD
  30.         prevChar: Integer;
  31.         followingByte: Integer;
  32.         next: Integer;
  33.         used: Boolean;
  34.         reserved: Boolean;
  35.     END;
  36.     StringTableArray = ARRAY [0..tableSize] OF StringTableEntry; {128K structure unless packed}
  37.     StringTablePtr = ^StringTableArray;
  38.  
  39.     IntPtr = ^Integer;
  40.     Buffer = PACKED ARRAY [1..maxBuff] OF Char;
  41.     BufPtr = ^Buffer;
  42.     HeaderRecord = RECORD
  43.         name: String[31];
  44.         dfSize: LongInt;
  45.         rfSize: LongInt;
  46.         fndrInfo: FInfo;
  47.     END;
  48.     StackType = ARRAY [1..maxStack] OF Integer;
  49.     StkPtr = ^StackType;
  50.     Remainder = (none, sixBit, fourBit, twoBit);
  51.  
  52. VAR
  53.     inRef: Integer;            {File reference number of the input file}
  54.     outRef: Integer;        {File reference number of the output file}
  55.     outVRefNum: Integer;    {Volume/WD reference number of output file}
  56.     eofSignal: Boolean;
  57.     inBufSize: Integer;        {Count of characters in the input buffer }
  58.     inputPos: Integer;        {Current position in the input buffer}
  59.     outputPos: Integer;        {Current position in the output buffer}
  60.     bytesRead: LongInt;        {Total bytes read from input file}
  61.     bytesWritten: LongInt;    {Total bytes written to output file}
  62.     bytesInBuffer: LongInt;    {Number of bytes read into input buffer at last attempt}
  63.     inputBuffer: BufPtr;    {Where we read the compressed data}
  64.     outputBuffer: BufPtr;    {Where we write the uncompressed data}
  65.     
  66.     stringTable: StringTablePtr;    {Pointer to memory structure}
  67.     outfileName: Str255;    {Name of file that we're recreating}
  68.     tableUsed: Integer;        {How many entries currently in string table}
  69.     inputCode: Integer;        {The 14-bit code that we're working on}
  70.     carryOver: Remainder;    {How many bits are to be prepended to next input byte}
  71.     doingDFork: Boolean;    {Flag to tell which fork of the file we're decompressing}
  72.     fsErr: OSErr;            {For file system calls}
  73.     dataForkSize: LongInt;    {Size of data fork we will decompress}
  74.     rsrcForkSize: LongInt;    {Size of resource fork we will decompress}
  75.     progWindow: WindowPtr;    {Window for debugging/progress information}
  76.     boundsRect: Rect;        {Rectangle for creating progress window}
  77.     stackPointer: Integer;    {Index into decode stack array}
  78.     stack: StkPtr;            {Pointer into decode stack array}
  79.     hdrRec: HeaderRecord;    {Our header that tells about the file we're decompressing}
  80.     
  81.     PROCEDURE _DataInit; EXTERNAL;    {Comment this out for THINK Pascal}
  82.     
  83.     
  84.     PROCEDURE FileAlert(str: Str255);
  85.     
  86.     CONST
  87.         fsAlert =    1111;
  88.     
  89.     VAR
  90.         item: Integer;
  91.     
  92.     BEGIN
  93.         ParamText(str, '', '', '');
  94.         item := StopAlert(fsAlert, NIL);
  95.         fsErr := FSClose(inRef);
  96.         fsErr := FSClose(outRef);
  97.         fsErr := FlushVol(NIL, outVRefnum);
  98.         ExitToShell;
  99.     END {FileAlert} ;
  100.     
  101.     
  102. {$IFC DEBUG}
  103.     PROCEDURE DebugAlert(l1, l2: LongInt);
  104.     
  105.     CONST
  106.         dbgAlert = 1112;
  107.     
  108.     VAR
  109.         s1, s2: Str255;
  110.         item: Integer;
  111.     
  112.     BEGIN
  113.         NumToString(l1, s1);
  114.         NumToString(l2, s2);
  115.         ParamText(s1, s2, '', '');
  116.         item := NoteAlert(dbgAlert, NIL);
  117.     END {DebugAlert} ;
  118. {$ENDC}
  119.  
  120.  
  121.     PROCEDURE ShowProgress;
  122.     
  123.     VAR
  124.         savePort: GrafPtr;
  125.         aStr: Str255;
  126.     
  127.     BEGIN
  128.         GetPort(savePort);
  129.         SetPort(progWindow);
  130.         EraseRect(progWindow^.portRect);
  131.         NumToString(bytesWritten, aStr);
  132.         MoveTo(5, 10);
  133.         DrawString(aStr);
  134.         NumToString(bytesRead, aStr);
  135.         MoveTo(5, 25);
  136.         DrawString(aStr);
  137.         NumToString(tableUsed, aStr);
  138.         MoveTo(5, 40);
  139.         DrawString(aStr);
  140.         SetPort(savePort);
  141.     END {ShowProgress} ;
  142.     
  143.     
  144.     FUNCTION HashIt(prevC, follC: Integer): Integer;
  145.     {You can come up with much better hash functions, just make sure that both
  146.      the compression and decompression programs use the same one.}
  147.     
  148.     VAR
  149.         temp,
  150.         local: LongInt;
  151.     
  152.     BEGIN
  153.         {local := BOR((prevC+follC), $00008000);
  154.         temp := local * local;
  155.         local := BAND(BSR(temp, 7), tableSize);}
  156.         HashIt := BAND(BXOR(BSL(prevC, 5), follC), tableSize);
  157.     END {HashIt} ;
  158.     
  159.     
  160.     FUNCTION GetHashCode(prevC, follC: Integer): Integer;
  161.     {    Return value is the hash code for <w>c string }
  162.     
  163.     VAR
  164.         index: Integer;
  165.         index2: Integer;
  166.     
  167.     BEGIN
  168.         index := HashIt(prevC, follC);
  169.         
  170.         {If the entry isn't already used we have a hash code}
  171.         IF (stringTable^[index].used) THEN BEGIN
  172.             {Entry already used, skip to end of collision list}
  173.             WHILE stringTable^[index].next <> endList DO
  174.                 index := stringTable^[index].next;
  175.             {Begin a linear probe down a bit from last entry in the collision list}
  176.             index2 := BAND(index + 101, tableSize);
  177.             {Look for an unused entry using linear probing}
  178.             WHILE stringTable^[index2].used DO
  179.                 index2 := BAND(Succ(index2), tableSize);
  180.             {Point the previous end of collision list at this new node}
  181.             stringTable^[index].next := index2;
  182.             GetHashCode := index2;
  183.         END ELSE GetHashCode := index;
  184.     END {GetHashCode} ;
  185.     
  186.     
  187.     PROCEDURE MakeTableEntry(prevC, follC: Integer);
  188.     {We could put the conditional test before each call to MakeTableEntry
  189.      instead of inside the routine}
  190.     
  191.     VAR
  192.         aCode: Integer;
  193.     
  194.     BEGIN
  195.         IF tableUsed <= tableSize THEN BEGIN
  196.             aCode := GetHashCode(prevC, follC);
  197.             WITH stringTable^[aCode] DO BEGIN
  198.                 used := true;
  199.                 next := endList;
  200.                 prevChar := prevC;
  201.                 followingByte := follC;
  202.             END;
  203.             
  204.             tableUsed := tableUsed + 1;
  205.         END;
  206.     END {MakeTableEntry} ;
  207.     
  208.     
  209.     FUNCTION LookupString(prevC, follC: Integer): Integer;
  210.     
  211.     VAR
  212.         index: Integer;
  213.         found: Boolean;
  214.     
  215.     BEGIN
  216.         index := HashIt(prevC, follC);
  217.         LookupString := endList;
  218.         found := FALSE;
  219.         {    Search list of collision entries for one that matches <w>c }
  220.         REPEAT
  221.             IF (stringTable^[index].prevChar = prevC) &
  222.                 (stringTable^[index].followingByte = follC) THEN found := true
  223.             ELSE index := stringTable^[index].next;
  224.         UNTIL found OR (index = endList);
  225.         { Return index if <w>c found, endList otherwise }
  226.         IF found THEN LookupString := index;
  227.     END {LookupString} ;
  228.     
  229.     
  230.     PROCEDURE GetByte(VAR c: Integer);
  231.     {    -- Read a character from the input file.  Make sure the compiler doesn't sign
  232.         -- extend anything.
  233.         -- Parameter
  234.         --    c    output
  235.         -- Globals affected
  236.         --    inputPos, bytesInBuffer, inputBuffer^ (global because no statics in Pascal)
  237.         --    bytesRead                    }
  238.     
  239.     VAR
  240.         count: LongInt;
  241.         error: OSErr;
  242.     
  243.     BEGIN
  244.         inputPos := inputPos + 1;
  245.         {    This will force a read the first time through and every time after that
  246.             where inputPos has "cycled back" to 0 }
  247.         IF inputPos > bytesInBuffer THEN BEGIN
  248.             bytesInBuffer := maxBuff;
  249.             error := FSRead(inRef, bytesInBuffer, Ptr(inputBuffer));
  250.             inputPos := 1;
  251.         END;
  252.         IF bytesInBuffer = 0 THEN BEGIN
  253.             c := eofChar;
  254.             eofSignal := true;
  255.         END ELSE BEGIN
  256.             bytesRead := bytesRead + 1;
  257.             c := Ord(inputBuffer^[inputPos]);
  258.         END;
  259.     END {GetByte} ;
  260.     
  261.     
  262.     PROCEDURE PutByte(c: Integer);
  263.     
  264.     VAR
  265.         count: LongInt;
  266.         error: OSErr;
  267.     
  268.     BEGIN
  269.         IF outputPos = maxBuff THEN BEGIN
  270.             count := maxBuff;
  271.             error := FSWrite(outRef, count, Ptr(outputBuffer));
  272.             outputPos := 0;
  273.             ShowProgress;
  274.         END;
  275.         IF doingDFork AND (bytesWritten >= dataForkSize) AND (NOT eofSignal) THEN BEGIN
  276.             doingDFork := false;
  277.             dataForkSize := bytesWritten;
  278.             IF outputPos > 0 THEN BEGIN
  279.                 count := outputPos;
  280.                 error := FSWrite(outRef, count, Ptr(outputBuffer));
  281.             END;
  282.             error := SetEOF(outRef, bytesWritten);
  283.             outputPos := 0;
  284.             error := FSClose(outRef);
  285.             IF rsrcForkSize > 0 THEN BEGIN
  286.                 {only need to open it if we have something to write}
  287.                 error := OpenRF(outfileName, outVRefNum, outRef);
  288.                 IF error <> noErr THEN FileAlert('Error opening resource fork');
  289.                 error := SetFPos(outRef, fsFromStart, 0);
  290.             END;
  291.         END;
  292.         outputPos := outputPos + 1;
  293.         outputBuffer^[outputPos] := Chr(c);
  294.         bytesWritten := bytesWritten + 1;
  295.     END {PutByte} ;
  296.     
  297.     
  298.     PROCEDURE InitStrTable;
  299.     
  300.     VAR
  301.         i: Integer;
  302.     
  303.     BEGIN
  304.         tableUsed := 0;
  305.         FOR i := 0 TO tableSize DO
  306.             WITH stringTable^[i] DO BEGIN
  307.                 prevChar := noPrev;
  308.                 followingByte := noPrev;
  309.                 next := -1;
  310.                 used := false;
  311.                 reserved := false;
  312.             END;
  313.         {Enter all single ascii characters into the string table}
  314.         FOR i := 0 TO clearCode DO
  315.             MakeTableEntry(noPrev, i);
  316.     END {InitStrTable} ;
  317.     
  318.     
  319.     PROCEDURE Initialize;
  320.     
  321.         PROCEDURE InitManagers;
  322.         
  323.         BEGIN
  324.             MaxApplZone;
  325.             InitGraf(@thePort);
  326.             InitFonts;
  327.             FlushEvents(everyEvent, 0);
  328.             InitWindows;
  329.             InitMenus;
  330.             TEInit;
  331.             InitDialogs(NIL);
  332.             InitCursor;
  333.             UnLoadSeg(@_DataInit);    {MPW-specific unload, comment out for THINK Pascal}
  334.         END {InitManagers} ;
  335.     
  336.     BEGIN
  337.         InitManagers;
  338.         
  339.         inputBuffer := BufPtr(NewPtr(SizeOf(Buffer)));
  340.         IF inputBuffer = NIL THEN ExitToShell;
  341.         outputBuffer := BufPtr(NewPtr(SizeOf(Buffer)));
  342.         IF outputBuffer = NIL THEN ExitToShell;
  343.         stringTable := StringTablePtr(NewPtr(SizeOf(StringTableArray)));
  344.         IF stringTable = NIL THEN ExitToShell;
  345.         
  346.         inputPos := 0;
  347.         outputPos := 0;
  348.         inBufSize := 0;
  349.         bytesRead := 0;
  350.         bytesWritten := 0;
  351.         bytesInBuffer := 0;
  352.         doingDFork := true;
  353.         inputCode := empty;
  354.         carryOver := none;
  355.         
  356.         InitStrTable;
  357.     END {Initialize} ;
  358.     
  359.     
  360.     PROCEDURE GetTopLeft({using} dlogID: Integer;
  361.                          {returning} VAR where: Point);
  362.     {    —    Return the point where DLOG(dlogID) should have its top-left corner so as
  363.         —    to be centered in the area below the menubar of the main screen.  The
  364.         —    centering is horizontal, vertically it should be one-third of the way.  This
  365.         —    is achieved by getting the DLOG resource and centering its rectangle within
  366.         —    screenBits.bounds after adjusting screenBits.bounds by mBarHeight. }
  367.     
  368.     CONST
  369.         {Probably should use Script Mgr. routine, GetMBarHeight, instead}
  370.         mBarHeight = $0BAA;    {Address of global integer containing menu bar height}
  371.     
  372.     VAR
  373.         screenRect,
  374.         dlogRect:    Rect;
  375.         mBarAdjustment: IntPtr;
  376.         aDlog: DialogTHndl;
  377.     
  378.     BEGIN
  379.         screenRect := screenBits.bounds;
  380.         mBarAdjustment := IntPtr(mBarHeight);
  381.         screenRect.top := screenRect.top + mBarAdjustment^;
  382.         aDlog := DialogTHndl(GetResource('DLOG', dlogID));
  383.         DetachResource(Handle(aDlog));
  384.         dlogRect := aDlog^^.boundsRect;
  385.         WITH screenRect DO BEGIN
  386.             where.v := ((bottom - top) - (dlogRect.bottom - dlogRect.top)) DIV 3;
  387.             where.h := ((right - left) - (dlogRect.right - dlogRect.left)) DIV 2;
  388.         END;
  389.     END {GetTopLeft};
  390.     
  391.  
  392.     FUNCTION GetInputFile({returning} VAR refNum: Integer): Boolean;
  393.     {    —    Return false if the user cancels, the request, true otherwise.  If a file
  394.         —    is selected for compression, open the file and pass back the refnum.
  395.         —    The constant getDlgID is from PackIntf.
  396.         —    Global side-effects of this routine include the initialization of a number
  397.         —    of fields of the hdrRec global and the setting of the inVRefNum global.}
  398.     
  399.     CONST
  400.         allFiles = -1;
  401.         
  402.     VAR
  403.         tl: Point;
  404.         reply: SFReply;
  405.         typeList: SFTypeList;
  406.         anErr,
  407.         error: OSErr;
  408.         finderInfo: FInfo;
  409.         count: LongInt;
  410.         dtRec: DateTimeRec;
  411.     
  412.     BEGIN
  413.         GetTopLeft(getDlgID, tl);
  414.         {typeList doesn't need to be initialized since we're asking for all files with the -1}
  415.         SFGetFile(tl, '', NIL, allFiles, typeList, NIL, reply);
  416.         IF reply.good THEN BEGIN
  417.             error := FSOpen(reply.fName, reply.vRefnum, refNum);
  418.             IF error = noErr THEN error := SetFPos(refNum, fsFromStart, 0)
  419.             ELSE anErr := FSClose(refNum);
  420.             IF error = noErr THEN BEGIN
  421.                 GetInputFile := true;
  422.                 count := SizeOf(HeaderRecord);
  423.                 error := FSRead(refNum, count, @hdrRec);
  424.                 IF error = noErr THEN BEGIN
  425.                     dataForkSize := hdrRec.dfSize;
  426.                     rsrcForkSize := hdrRec.rfSize;
  427.                 END ELSE BEGIN
  428.                     anErr := FSClose(refNum);
  429.                     GetInputFile := false;
  430.                 END;
  431.             END ELSE GetInputFile := false;
  432.         END ELSE GetInputFile := false;
  433.     END {GetInputFile} ;
  434.     
  435.     
  436.     FUNCTION GetOutputFile({returning} VAR refNum: Integer): Boolean;
  437.     
  438.     VAR
  439.         tl: Point;
  440.         reply: SFReply;
  441.         error: OSErr;
  442.         count: LongInt;
  443.     
  444.     BEGIN
  445.         GetTopLeft(putDlgID, tl);
  446.         SFPutFile(tl, '', hdrRec.name, NIL, reply);
  447.         IF reply.good THEN BEGIN
  448.             outfileName := reply.fName;
  449.             error := FSOpen(reply.fName, reply.vRefnum, refNum);
  450.             IF error <> noErr THEN BEGIN    {File didn't already exist, need to create it}
  451.                 error := Create(reply.fName, reply.vRefnum,
  452.                                 hdrRec.fndrInfo.fdCreator, hdrRec.fndrInfo.fdType);
  453.                 
  454.                 IF error = noErr THEN 
  455.                     IF hdrRec.dfSize > 0 THEN
  456.                         error := FSOpen(reply.fName, reply.vRefnum, refNum)
  457.                     ELSE BEGIN
  458.                         error := OpenRF(reply.fName, reply.vRefNum, refNum);
  459.                         doingDFork := false;
  460.                     END;
  461.                 IF error = noErr THEN error := SetFPos(refNum, fsFromStart, 0);
  462.             END;
  463.             IF error = noErr THEN BEGIN
  464.                 GetOutputFile := true;
  465.                 outVRefNum := reply.vRefnum;
  466.             END ELSE GetOutputFile := false;
  467.         END ELSE GetOutputFile := false;
  468.     END {GetOutputFile} ;
  469.     
  470.  
  471.     PROCEDURE Terminate;
  472.     
  473.     VAR
  474.         count: LongInt;
  475.     
  476.     BEGIN
  477.         ShowProgress;
  478.         IF outputPos > 0 THEN BEGIN
  479.             count := outputPos;
  480.             fsErr := FSWrite(outRef, count, Ptr(outputBuffer));
  481.             IF fsErr = noErr THEN BEGIN
  482.                 IF doingDFork THEN BEGIN
  483.                     dataForkSize := bytesWritten;
  484.                     fsErr := SetEOF(outRef, dataForkSize);
  485.                 END ELSE IF rsrcForkSize > 0 THEN BEGIN
  486.                     rsrcForkSize := bytesWritten - dataForkSize;
  487.                     fsErr := SetEOF(outRef, rsrcForkSize);
  488.                 END;
  489.                 IF fsErr <> noErr THEN FileAlert('SetEOF Error in Terminate');
  490.             END ELSE FileAlert('Write Error in Terminate');
  491.         END;
  492.         fsErr := FSClose(outRef);
  493.         fsErr := FlushVol(NIL, outVRefNum);
  494.         fsErr := FSClose(inRef);
  495.     END {Terminate} ;
  496.     
  497.     
  498.     PROCEDURE GetCode(VAR hashCode: Integer);
  499.     
  500.     VAR
  501.         localBuf, localBuf2: Integer;
  502.     
  503.     BEGIN
  504.         CASE carryOver OF
  505.             none:    {get two bytes and return 14 ms bits, carry over two least}
  506.                 BEGIN
  507.                     GetByte(localBuf);
  508.                     IF (localBuf = eofChar) THEN BEGIN
  509.                         hashCode := eofChar;
  510.                         Exit(GetCode);
  511.                     END;
  512.                     GetByte(inputCode);
  513.                     IF (inputCode = eofChar) THEN BEGIN
  514.                         hashCode := eofChar;
  515.                         Exit(GetCode);
  516.                     END;
  517.                     hashCode := BAND(BSL(localBuf, 6), $3FC0) +
  518.                                 BAND(BSR(inputCode, 2), $003F);
  519.                     inputCode := BAND(inputCode, $0003);
  520.                     carryOver := twoBit;
  521.                 END;
  522.             
  523.             twoBit:    {have two bits, get two bytes, return 14 ms bits, save 4 ls bits}
  524.                 BEGIN
  525.                     GetByte(localBuf);
  526.                     IF (localBuf = eofChar) THEN BEGIN
  527.                         hashCode := eofChar;
  528.                         Exit(GetCode);
  529.                     END;
  530.                     GetByte(localBuf2);
  531.                     IF (localBuf2 = eofChar) THEN BEGIN
  532.                         hashCode := eofChar;
  533.                         Exit(GetCode);
  534.                     END;
  535.                     hashCode := BAND(BSL(inputCode, 12), $3000) +
  536.                                 BAND(BSL(localBuf, 4), $0FF0) +
  537.                                 BAND(BSR(localBuf2, 4), $000F);
  538.                     inputCode := BAND(localBuf2, $000F);
  539.                     carryOver := fourBit;
  540.                 END;
  541.             
  542.             fourBit: {Have four bits, get two bytes, return 14 ms bits, save 6 ls bits}
  543.                 BEGIN
  544.                     GetByte(localBuf);
  545.                     IF (localBuf = eofChar) THEN BEGIN
  546.                         hashCode := eofChar;
  547.                         Exit(GetCode);
  548.                     END;
  549.                     GetByte(localBuf2);
  550.                     IF (localBuf2 = eofChar) THEN BEGIN
  551.                         hashCode := eofChar;
  552.                         Exit(GetCode);
  553.                     END;
  554.                     hashCode := BAND(BSL(inputCode, 10), $3C00) +
  555.                                 BAND(BSL(localBuf, 2), $03FC) +
  556.                                 BAND(BSR(localBuf2, 6), $0003);
  557.                     inputCode := BAND(localBuf2, $003F);
  558.                     carryOver := sixBit;
  559.                 END;
  560.             
  561.             sixBit:    {have six bits, get a byte, return the 14 bits, carry nothing}
  562.                 BEGIN
  563.                     GetByte(localBuf);
  564.                     IF (localBuf = eofChar) THEN BEGIN
  565.                         hashCode := eofChar;
  566.                         Exit(GetCode);
  567.                     END;
  568.                     hashCode := BAND(BSL(inputCode, 8), $3F00) +
  569.                                 BAND(localBuf, $00FF);
  570.                     inputCode := empty;
  571.                     carryOver := none;
  572.                 END;
  573.         END;
  574.     END {GetCode} ;
  575.     
  576.     
  577.     PROCEDURE Push(c: Integer);
  578.     
  579.     BEGIN
  580.         stackPointer := stackPointer + 1;
  581.         stack^[stackPointer] := c;
  582.  
  583.         IF (stackPointer >= maxStack) THEN BEGIN
  584.             {If this happens, you've typed something in wrong -- would take
  585.              a degenerate case of over 16MB in size to do so otherwise}
  586.             FileAlert('***STACK OVERFLOW***');
  587.         END;
  588.     END {Push} ;
  589.     
  590.     
  591.     PROCEDURE Pop(VAR c: Integer);
  592.     
  593.     BEGIN
  594.         IF stackPointer > 0 THEN BEGIN
  595.             c := stack^[stackPointer];
  596.             stackPointer := stackPointer - 1;
  597.         END ELSE c := empty;
  598.     END {Pop} ;
  599.     
  600.  
  601.     PROCEDURE DoDecompression;
  602.     
  603.     VAR
  604.         c: Integer;
  605.         code: Integer;
  606.         oldCode: Integer;
  607.         finalByte: Integer;
  608.         inCode: Integer;
  609.         lastChar: Integer;
  610.         unknown: Boolean;
  611.         tempC: Integer;
  612.         resetCode: Integer;
  613.         anEvent: EventRecord;
  614.     
  615.     BEGIN
  616.         {Initialize things and "prime the pump"}
  617.         stackPointer := 0;
  618.         stack := StkPtr(NewPtr(SizeOf(StackType)));
  619.         unknown := false;    {First string is always known as it is a single char}
  620.         resetCode := LookupString(noPrev, clearCode);
  621.         GetCode(oldCode);
  622.         code := oldCode;
  623.         c := stringTable^[code].followingByte;
  624.         PutByte(c);
  625.         finalByte := c;
  626.         
  627.         {Now, we get down to work}
  628.         GetCode(inCode);
  629.         WHILE inCode <> eofChar DO BEGIN
  630.             code := inCode;
  631.             IF (NOT stringTable^[code].used) THEN BEGIN
  632.                 lastChar := finalByte;
  633.                 code := oldCode;
  634.                 unknown := true;
  635.             END;
  636.             
  637.             {    Run through code extracting single bytes until no more
  638.                 bytes can be removed.  Push these onto the stack.  They
  639.                 will be entered in reverse order and will come out in proper
  640.                 order when popped. }
  641.             WHILE (stringTable^[code].prevChar <> noPrev) DO
  642.                 WITH stringTable^[code] DO BEGIN
  643.                     Push(followingByte);
  644.                     code := prevChar;
  645.                 END;
  646.             
  647.             {    We now have the first byte in the string. }
  648.             finalByte := stringTable^[code].followingByte;
  649.             PutByte(finalByte);
  650.             {    Now pop everything off the stack }
  651.             Pop(tempC);
  652.             WHILE tempC <> empty DO BEGIN
  653.                 PutByte(tempC);
  654.                 Pop(tempC);
  655.             END;
  656.             {    If the code isn't known, then output the follower byte of
  657.                 the last byte in the string. }
  658.             IF unknown THEN BEGIN
  659.                 finalByte := lastChar;
  660.                 PutByte(finalByte);
  661.                 unknown := false;
  662.             END;
  663.             
  664.             IF GetNextEvent(everyEvent, anEvent) THEN ;
  665.             MakeTableEntry(oldCode, finalByte);
  666.             oldCode := inCode;
  667.             GetCode(inCode);
  668.             IF (inCode = resetCode) THEN BEGIN
  669.                 {Compression ratio dropped, time to build a new table}
  670.                 InitStrTable;
  671.                 GetCode(oldCode);
  672.                 c := stringTable^[oldCode].followingByte;
  673.                 PutByte(c);
  674.                 finalByte := c;
  675.                 GetCode(inCode);
  676.             END;
  677.         END;
  678.     END {DoDecompression} ;
  679.  
  680. BEGIN
  681.     Initialize;
  682.     IF GetInputFile(inRef) THEN
  683.         IF GetOutputFile(outRef) THEN BEGIN
  684.             SetRect(boundsRect, 100, 50, 250, 100);
  685.             progWindow := NewWindow(NIL, boundsRect, 'Bytes Read',
  686.                                     true, noGrowDocProc, Pointer(-1), false, 0);
  687.             DoDecompression;
  688.             Terminate;
  689. {$IFC DEBUG}
  690.             DebugAlert(bytesRead, bytesWritten);
  691. {$ENDC}
  692.         END;
  693. END.